package com.maodou.v2ray

import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import com.google.gson.GsonBuilder
import com.maodou.v2ray.dto.AppConfig
import com.maodou.v2ray.dto.V2rayConfig
import com.maodou.v2ray.util.Utils
import go.Seq
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import libv2ray.Libv2ray
import libv2ray.V2RayPoint
import libv2ray.V2RayVPNServiceSupportsSet
import java.lang.ref.SoftReference

object V2RayServiceManager {



    val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1)
    private val TAG = "V2RayServiceManager"

    var serviceControl: SoftReference<ServiceControl>? = null
        set(value) {
            field = value
            Seq.setContext(value?.get()?.getService()?.applicationContext)
            Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()), Utils.getDeviceIdForXUDPBaseKey())
            //Libv2ray.initV2Env(Utils.userAssetPath(value?.get()?.getService()))

        }
    var currentConfig: V2rayConfig? = null
    private var lastQueryTime = 0L

    fun startV2Ray(context: Context, config: String, allowPackages: ArrayList<String> = arrayListOf(), isVpn: Boolean = true, tunnelAddress: String? = null) {
        if (v2rayPoint.isRunning) return

        val intent = if (isVpn) {
            Intent(context.applicationContext, V2RayVpnService::class.java)
        } else {
            Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
        }

        intent.putExtra(AppConfig.EXTRA_V2RAY_PROXY_CONFIG , config)
        intent.putStringArrayListExtra(AppConfig.EXTRA_ALLOW_PACKAGES, allowPackages)
        tunnelAddress?.let {
            intent.putExtra(AppConfig.EXTRA_TUNNEL_INTF_ADDRESS, tunnelAddress)
        }
        context.startService(intent)
    }

    private class V2RayCallback : V2RayVPNServiceSupportsSet {
        override fun shutdown(): Long {
            val serviceControl = serviceControl?.get() ?: return -1
            // called by go
            return try {
                serviceControl.stopService()
                0
            } catch (e: Exception) {
                Log.d(TAG, e.toString())
                -1
            }
        }

        override fun prepare(): Long {
            return 0
        }

        override fun protect(l: Long): Boolean {
            val serviceControl = serviceControl?.get() ?: return true
            return serviceControl.vpnProtect(l.toInt())
        }

        override fun onEmitStatus(l: Long, s: String?): Long {
            Log.d(TAG, "xray core onEmitStatus，status：$l，msg：$s")
            return 0
        }

        override fun setup(s: String): Long {
            val serviceControl = serviceControl?.get() ?: return -1
            //Logger.d(s)
            return try {
                serviceControl.startService()
                lastQueryTime = System.currentTimeMillis()
                0
            } catch (e: Exception) {
                Log.d(TAG, e.toString())
                -1
            }
        }
    }

    fun startV2rayPoint(intent: Intent?) {
        val service = serviceControl?.get()?.getService() ?: return
        if (v2rayPoint.isRunning || intent == null) {
            return
        }

        val gson = GsonBuilder().create()
        val proxyConfig = intent.getStringExtra(AppConfig.EXTRA_V2RAY_PROXY_CONFIG)
        val v2rayConfig = gson.fromJson(proxyConfig, V2rayConfig::class.java)

        v2rayPoint.configureFileContent = proxyConfig
        v2rayPoint.domainName = v2rayConfig.getV2rayPointDomainAndPort()
        currentConfig = v2rayConfig

        try {
            v2rayPoint.runLoop(false)
        } catch (e: Exception) {
            Log.d(TAG, e.toString())
        }

    }

    fun stopV2RayService(){
        serviceControl?.get()?.stopService()
    }

    fun stopV2rayPoint() {
        val service = serviceControl?.get()?.getService() ?: return

        if (v2rayPoint.isRunning) {
            GlobalScope.launch(Dispatchers.Default) {
                try {
                    v2rayPoint.stopLoop()
                } catch (e: Exception) {
                    Log.d(TAG, e.toString())
                }
            }
        }
    }


}
